package com.rainman.simpleredis.core;

import cn.hutool.json.JSONObject;
import com.rainman.simpleredis.core.config.SimpleRedisConfig;
import com.rainman.simpleredis.core.delay_work.SimpleDelayWorkMsgBean;
import com.rainman.simpleredis.core.delay_work.SimpleDelayWorkService;
import com.rainman.simpleredis.core.distributed_lock.SimpleDistributedLock;
import com.rainman.simpleredis.core.process_flow.*;
import com.rainman.simpleredis.core.scheduled.SimpleScheduled;
import com.rainman.simpleredis.core.scheduled.SimpleScheduledWorkerInterface;
import com.rainman.simpleredis.core.simplemq.SimpleMqRealServer;
import com.rainman.simpleredis.core.simplemq.SimpleMqSerialServer;
import com.rainman.simpleredis.core.unique_id.SimpleUniqueId;

import java.util.List;
import java.util.concurrent.*;

public class SimpleRedis {

    private final SimpleRedisConfig simpleRedisConfig;

    private final SimpleScheduled simpleScheduled;
    private final SimpleMqRealServer simpleMqRealServer;
    private final SimpleMqSerialServer simpleMqSerialServer;
    private final SimpleDelayWorkService simpleDelayWorkService;
    private final SimpleProcessFlow simpleProcessFlow;
    private final SimpleDistributedLock simpleDistributedLock;
    private final SimpleUniqueId simpleUniqueId;


    public SimpleRedis(SimpleRedisConfig simpleRedisConfig){
        this.simpleRedisConfig = simpleRedisConfig;

        if(simpleRedisConfig ==null|| simpleRedisConfig.getRedisInterface()==null){
            throw new RuntimeException("must init RedisInterface");
        }

        simpleDistributedLock=new SimpleDistributedLock(simpleRedisConfig);
        simpleScheduled=new SimpleScheduled(simpleRedisConfig,simpleDistributedLock);
        simpleMqRealServer=new SimpleMqRealServer(simpleRedisConfig,simpleDistributedLock,simpleScheduled);
        simpleMqSerialServer=new SimpleMqSerialServer(simpleRedisConfig,simpleDistributedLock,simpleScheduled);
        simpleDelayWorkService=new SimpleDelayWorkService(simpleRedisConfig,simpleDistributedLock,simpleScheduled);
        simpleProcessFlow=new SimpleProcessFlow(simpleRedisConfig,simpleDistributedLock,simpleScheduled);
        simpleUniqueId=new SimpleUniqueId(simpleRedisConfig,simpleScheduled);

    }

    /**
     * 发布一条即时串行处理消息
     * 注意相同targetType targetId会被覆盖
     *
     * @param targetType 消息类型
     * @param targetId 目标id
     * @param msgStr 消息字符串
     */
    public void publishOne(String targetType,
                           String targetId,
                           String msgStr){

        simpleMqSerialServer.publishMQSerialOri(targetType,targetId,msgStr);

    }



    /**
     * 发布一条即时并行处理消息
     * 注意相同targetType targetId会被覆盖
     * 留意事务
     *
     * @param targetType 消息类型
     * @param targetId 目标id
     * @param msgStr 消息字符串
     */
    public void publishOnePara(String targetType,
                           String targetId,
                           String msgStr){



        simpleMqRealServer.publishMQOri(targetType,targetId,msgStr);

    }


    /**
     * 动态新增定时任务
     *
     * @param workerInterface
     */
    public void addSchWorker(SimpleScheduledWorkerInterface workerInterface){

        simpleScheduled.addWorker(workerInterface);
    }

    /**
     * 去除任务
     * 删除任务源数据
     * 所有终端都不会再触发任务
     * 注意
     *  有包含该key的任意任务终端重启，或重新上线该操作会失效
     *
     * @param key
     */
    public void removeSch(String key){

        simpleScheduled.removeWorker(key);
    }


    /**
     *
     * 监听过期key
     * 也可手动调用模拟
     *
     * @param key 过期的key
     */
    public void dealTimeOutTig(String key){

        if(key==null){
            return;
        }

        if(key.startsWith(simpleScheduled.ScheduledTig)){
            //定时任务
            simpleScheduled.dealOne(key);

        }else if(key.startsWith(simpleProcessFlow.TigListenerTimeOutHead)){
            //流任务，监听器过期
            simpleProcessFlow.dealListenerTimeOut(key);

        }else if(key.startsWith(simpleDelayWorkService.DelayWorkTig)){
            //延迟任务

            simpleDelayWorkService.dealOne(key);

        }

    }

    /**
     *
     * 监听sub
     * 也可手动调用模拟
     *
     * @param key 过期的key
     */
    public void dealSubTig(String key){

        if(key==null){
            return;
        }

        if(key.startsWith(simpleMqRealServer.RealTimeHead)){
            //消费实时数据

            simpleMqRealServer.dealOne(key);

        }else if(key.startsWith(simpleMqSerialServer.SerialIndex)){
            //消费串行数据

            simpleMqSerialServer.readOne(key);

        }else if(key.startsWith(simpleProcessFlow.ProcessFlowTigOriHead)){
            //消费流式数据

            simpleProcessFlow.dealOne(key);

        }

    }

    /**
     * 分布式悲观锁
     *
     * @param key 键，最终入库会加上头 RS0.DLock.加上这个参数key
     * @param dur 过期毫秒数
     * @return 是否上锁成功
     */
    public Boolean dLock(String key,Long dur){

        return simpleDistributedLock.dLockAdd(key,dur);
    }

    /**
     * 删除分布式悲观锁
     * @param key 最终会删除 加上头 RS0.DLock.加上这个参数key
     * @return 解锁成功
     */
    public Boolean dLockRemove(String key ){

        return simpleDistributedLock.dLockRemove(key);

    }

    /**
     * 是否有锁
     * @param key
     * @return
     */
    public Boolean dLockHasLock(String key ){

        return simpleDistributedLock.hasLock(key);

    }

    /**
     * 是否有锁，通配
     * @param keyBeginStr 匹配键的开头部分
     * @return
     */
    public Boolean dLockHasLockMatch(String keyBeginStr ){

        return simpleDistributedLock.hasLockMatch(keyBeginStr);

    }

    /**
     * 新增延迟任务
     *
     * @param targetType
     * @param targetId
     * @param targetInfo
     * @param tigTime
     *
     * @return 唯一id
     */
    public SimpleDelayWorkMsgBean addDelayWork(String targetType, String targetId, String targetInfo, Long tigTime){
        return simpleDelayWorkService.addOne(targetType, targetId, targetInfo, tigTime);
    }

    /**
     * 新增流式任务
     *
     * @param listener 监听可为空
     *
     */
    public void addProcessFlow(ProcessFlowBuilder processFlowBuilder,SimpleProcessFlowListener listener){

        simpleProcessFlow.addOne( processFlowBuilder,listener);
    }


    public void addProcessFlowNoReturn(ProcessFlowBuilder processFlowBuilder){

        simpleProcessFlow.addOne(processFlowBuilder,null);
    }

    /**
     * 新增流式任务并等待结果
     *
     * @param processFlowBuilder
     * @return
     */
    public ProcessFlowReturn addProcessFlow(ProcessFlowBuilder processFlowBuilder){
        CountDownLatch cdl = new CountDownLatch(1);

        ProcessFlowReturn processFlowReturn=new ProcessFlowReturn();
        processFlowReturn.setKey(processFlowBuilder.getKey());

        simpleProcessFlow.addOne(processFlowBuilder, new SimpleProcessFlowListener() {
            @Override
            public void onDoing(String key, Integer index, String type) {

                System.out.println(key+" onDoing "+index);
            }

            @Override
            public void onSucc(String key, List<String> returnInfo) {
                processFlowReturn.setState(ProcessFlowReturn.StateOK);
                processFlowReturn.setReturnInfo(returnInfo);
                cdl.countDown();

            }

            @Override
            public void onErr(String key, String msg) {

                processFlowReturn.setState(ProcessFlowReturn.StateErr);
                processFlowReturn.setErrMsg(msg);
                cdl.countDown();
            }

        });


        try {
            cdl.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return processFlowReturn;
    }

    /**
     * 获取一个唯一id
     * 使用的是redis的健递增，性能一般
     * 每秒最多生成996个，
     * 需要高性能的可以使用雪花算法的其他方式生产
     *
     * @return
     */
    public Long uniqueIdGetOne(){
        return simpleUniqueId.getOne();
    }

    /**
     * 获取配置
     * @return
     */
    public SimpleRedisConfig getSimpleConfig() {
        return simpleRedisConfig;
    }


    //------------------------------

    //流程流转

    //多点确认  list  保存总流程   单条保存单个节点流程

    //总流程有超时时间   单个节点确认

    //过期后如果有流程失败或者超时，则单个反向确认完结

    //座位系统锁坐 订单系统下单 用户系统扣除抵扣积分  优惠券系统使用优惠券 三方支付系统下单 通知系统通知商户

    //各系统各自有各自的事务
    //如果三方支付系统出错

    //优惠券系统取消使用优惠券 用户系统取消扣除抵扣积分 订单取消系统下单 座位系统取消锁坐

    //一般都会取消成功 取消成功则完结  要保证顺序正确 并且收到并确认执行  无需重试机制

    //若有取消失败的则进入人工处理



    //悲观锁 实现





}
